package util.parser.wikiXML;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;


//import com.sun.org.apache.xalan.internal.xsltc.runtime.Attributes;

import util.parser.wikiXML.*;
import util.wikiXML.*;
import util.wikiXML.objectModel.WikiContainer;
import util.wikiXML.objectModel.WikiLink;
import util.wikiXML.objectModel.WikiPage;
import util.wikiXML.objectModel.WikiParagraph;
import util.wikiXML.objectModel.WikiSection;
import util.wikiXML.objectModel.WikiTable;
import util.wikiXML.objectModel.WikiTableCell;
import util.wikiXML.objectModel.WikiTemplate;
import util.wikiXML.objectModel.WikiText;
import util.wikiXML.objectModel.WikiPage.*;


import nu.xom.*;

/**
 * 
 * @author Sergio
 * 
 */
public class WikiParserXOM {

	/**
	 * @param args
	 */

	private WikiPage wiki;
	private List<WikiContainer> sections = new LinkedList<WikiContainer>();

	private LinkedList<WikiContainer> current_container = new LinkedList<WikiContainer>();
	
	
	

	
	public WikiPage parseWiki(String file_) {
		File file = new File(file_);
		Builder builder = new Builder();
		try {

			Document doc = builder.build(file);
			Element root = doc.getRootElement();

			listChildren(root, 0);
		}
		// indicates a well-formedness error
		catch (ParsingException ex) {
			System.out.println(file + " is not well-formed.");
			System.out.println(ex.getMessage());
			wiki = null;
		} catch (IOException ex) {
			System.out.println(ex);
			wiki = null;
		}

		return wiki;
	}

	

	
	public void listChildren(Node current, int depth) {

		printSpaces(depth);
		String data = "";
		if (current instanceof Element) {
			Element temp = (Element) current;
			data = ": " + temp.getQualifiedName();

			elementListenerPre(temp, depth);

		} else if (current instanceof ProcessingInstruction) {
			ProcessingInstruction temp = (ProcessingInstruction) current;
			data = ": " + temp.getTarget();
		} else if (current instanceof DocType) {
			DocType temp = (DocType) current;
			data = ": " + temp.getRootElementName();
		} else if (current instanceof Text || current instanceof Comment) {
			String value = current.getValue();
			value = value.replace('\n', ' ').trim();

			if (value != null && !value.equals("")
					&& current_container.size() > 0) {
				WikiContainer container = current_container.pop();
				System.out.println("");
				System.out.println("+++++++  Adding text +++++++");
				System.out
						.println(container.getClass().getName() + ":" + value);
				
				WikiText text = new WikiText();
				text.setText(value);
				container.addElement(text);
				current_container.push(container);
			}

			if (value.length() <= 50)
				data = ": " + value;
			else
				data = ": " + current.getValue().substring(0, 47) + "...";
		}

		// Attributes are never returned by getChild()

		// System.out.println(current.getClass().getName() + ":"+ data);
		for (int i = 0; i < current.getChildCount(); i++) {
			listChildren(current.getChild(i), depth + 1);
		}

		/**
		 * Functions performed after is processed the whole tree under the
		 * current node
		 */

		if (current instanceof Element) {
			Element temp = (Element) current;
			data = ": " + temp.getQualifiedName();

			elementListenerPost(temp, depth);

		}
	}

	private void elementListenerPre(Element temp, int depth) {
		// TODO Auto-generated method stub

		String tag = temp.getQualifiedName();

		/**
		 *title, meta We process the header of the XML
		 * 
		 * We create the WikiPage object, we specify the title, pagename and id
		 * 
		 */
		if (tag.equals("title")) {

			wiki = new WikiPage();

			wiki.setTitle(temp.getValue());

		} else if (tag.equals("meta")) {

			if (XOMParserUtils.hasAttribute("wx_pagename", temp)) {
				wiki.setName(temp.getAttributeValue("content"));

			} else if (XOMParserUtils.hasAttribute("wx_page_id", temp)) {
				wiki.setId(temp.getAttributeValue("content"));

			}

		}

		/**
		 * wx:section
		 * 
		 * We add a section to the current_container LIFO structure (sections
		 * can be nested)
		 * 
		 * 
		 */
		else if (tag.equals("wx:section")) {
			WikiSection section = new WikiSection();
			section.setName(temp.getAttributeValue("title"));
			section.setLevel(Integer.valueOf(temp.getAttributeValue("level"))
					.shortValue());

			sections.add(section);
			
			
		}

		/**
		 * p paragraphs
		 * 
		 * 
		 */

		else if (tag.equals("p")) {
			WikiParagraph p = new WikiParagraph();

			// revise
			p.setText(temp.getValue());

			current_container.push(p);
		}

		/**
		 * a links
		 */

		else if (tag.equals("a")) {
			parseLinkPre(temp);

		}

		/**
		 * table tag
		 * 
		 */
		else if (tag.equals("table")) {

			parseTablePre(temp);

		}

		/**
		 * <TR>
		 * tag We create new row in the current table
		 */

		else if (tag.equals("tr")) {

			parseTRPre(temp);

		}

		/**
		 * <TH>
		 * We add header cell to the table
		 */

		else if (tag.equals("th")) {

			parseTHPre(temp);

		}

		/**
		 * <TD>
		 * 
		 * We add a data cell to the current row
		 * 
		 */

		else if (tag.equals("td")) {

			parseTDPre(temp);

		}
		
		
		/**
		 * div element
		 * In this tag we parse the categoryLinks, crossLanguageLinks
		 * 
		 * We ignore the div tags that refer to stubs and other Wikipedia
		 * administrative elements
		 * 
		 */
		else if (tag.equals("div")) {

			
			parseDivPre(temp);
			
		}
				
		/**
		 * wx:template 
		 * We parse the template tag, creating WikiTemplate objects containing
		 * the elements between <wx:template and <wx:templateend/> 
		 * 
		 * We also add the corresponding parsed values given in the lowest part 
		 * of the Wikipage (the wx:templatearguments elements)
		 */

			
		else if(tag.equals("wx:template")){
			
			parseTemplatePre(temp);
					
		}
		
		
	else if (tag.equals("wx:templateend")) {

			WikiContainer current = current_container.pop();

			if (current instanceof WikiTemplate) {

				if (current_container.size() > 0) {
					System.out.println("Format exception: Section expected");

				} else {
					updateCurrentSection(current, sections);
				}

			} else {

				System.out.println("Format exception:WikiTemplate expected");

			}

		}
		
		
		 
		/**
		 * HTML text format tags: <b>, <i>, and so on Defined in
		 * HTMLElementsList
		 * 
		 */

		else if (XOMParserUtils.isHTMLElementTextFormat(tag)) {
			parseFormattedText(temp, tag);

		}

	}

	private void parseDivPre(Element temp) {
		// TODO Auto-generated method stub
	
		if (XOMParserUtils.hasAttributeToBeFilter(temp, "id")) {
			// check effect of removeChildren
			temp.removeChildren();
		}

		else if (temp.getAttributeValue("id").equals("wx_categorylinks")) {

			parseCategoryLinks(temp);
		} else if (temp.getAttributeValue("id").equals("wx_languagelinks")) {
			parseLanguageLinks(temp);
		}
		
	}

	private void parseTemplatePre(Element temp) {
		// TODO Auto-generated method stub
		
		WikiTemplate template= new WikiTemplate();
		
		template.setPageName(temp.getAttributeValue("pagename"));
		template.setPageId(temp.getAttributeValue("page_id"));
		template.setTemplateInternalId(temp.getAttributeValue("id"));		
		
		current_container.add(template);
				
	}

	private void parseLanguageLinks(Element temp) {
		// TODO Auto-generated method stub
		for (int i = 0; i < temp.getChildCount(); i++) {
			Node node = temp.getChild(i);
			if (node instanceof Element) {
				Element element = (Element) node;
				if (element.getQualifiedName().equals("a")) {
					
					WikiLink link = createLink(element);
					System.out.println("Language:" + link.getText());
					wiki.cross_lingual.add(link);
				}
			}
		}
	}

	private void parseCategoryLinks(Element temp) {
		// TODO Auto-generated method stub

		// We iterate through all the children of the current element
		for (int i = 0; i < temp.getChildCount(); i++) {
			Node node = temp.getChild(i);
			if (node instanceof Element) {
				Element element = (Element) node;
				if (element.getQualifiedName().equals("span")) {
					for (int j = 0; j < element.getChildCount(); j++) {
						{
							Node node2 = element.getChild(j);
							if (node2 instanceof Element) {
								Element element2 = (Element) node2;
								if (element2.getQualifiedName().equals("a")) {

									WikiLink link = createLink(element2);
									wiki.categories.add(link);
								}
							}
						}
					}
				}
			}
		}
	}

	private void parseTHPre(Element temp) {
		// TODO Auto-generated method stub

		WikiContainer container = current_container.pop();
		if (container instanceof WikiTable) {
			WikiTable table = (WikiTable) container;

			if (table.getLastElement() instanceof LinkedList) {
				LinkedList<WikiTableCell> row = (LinkedList<WikiTableCell>) table
						.getLastElement();
				WikiTableCell cell = new WikiTableCell();
				cell.setHeader(true);
				// Check this one
				cell.setValue(temp.getValue());
				row.add(cell);
				current_container.push(container);
				current_container.push(cell);
				return;
			} else {

				System.out.println("Table format error in TH");
			}

		} else {

			System.out
					.println("Table format error in TH, not WikiTable object: "
							+ container.getClass());
		}

		current_container.push(container);
	}

	private void parseTDPre(Element temp) {
		// TODO Auto-generated method stub
		WikiContainer container = current_container.pop();

		if (container instanceof WikiTable) {
			WikiTable table = (WikiTable) container;
			// System.out.println("&&&&&&&&&  Elements of table &&&&&&&");
			// XOMParserUtils.printStackClasses(table.getElements());
			// System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&");

			if (table.getLastElement() instanceof LinkedList) {

				LinkedList<WikiTableCell> row = (LinkedList<WikiTableCell>) table
						.getLastElement();

				WikiTableCell cell = new WikiTableCell();
				// Check this
				cell.setHeader(false);
				cell.setValue(temp.getValue());
				row.add(cell);

				current_container.push(container);
				current_container.push(cell);
				return;
			} else {

				System.out
						.println("Table format error in TD, systen obtained: "
								+ table.getLastElement().getClass());
			}
		} else {

			System.out
					.println("Table format error in TD, not WikiTable object: "
							+ container.getClass());
		}

		current_container.push(container);
	}

	private void parseTRPre(Element temp) {
		// TODO Auto-generated method stub

		WikiContainer container = current_container.pop();
		WikiTable table;
		if (container instanceof WikiTable) {
			table = (WikiTable) container;
			LinkedList<WikiTableCell> row = new LinkedList<WikiTableCell>();
			table.addElement(row);
		} else {

			System.out.println("Table format error in TR ");
			System.out.println(temp.getValue());
		}
		current_container.push(container);
	}

	private void parseTablePre(Element temp) {
		// TODO Auto-generated method stub
		
		ArrayList a = new ArrayList();
		
		WikiTable table = new WikiTable();

		if (XOMParserUtils.hasAttribute("class", temp)) {
			table.setTable_class(temp.getAttributeValue("class"));

		}
		current_container.push(table);

	}

	private void parseFormattedText(Element temp, String tag) {
		// TODO Auto-generated method stub

		WikiText text = new WikiText();
		
		text.setFormat_code((short) XOMParserUtils
				.getHTMLElementTextFormatIndex(tag));

		text.setText(temp.getValue());
		
		
		current_container.push(text);

		/*
		 * if(current_container.size()>0){ WikiContainer current =
		 * current_container.pop(); current.addElement(text);
		 * current_container.push(current); }
		 */

	}
		
	private void parseLinkPre(Element temp){
				
		WikiLink link= createLink(temp);
				
		if (current_container.size() > 0) {
			WikiContainer current = current_container.pop();
			current.addElement(link);
			current_container.push(current);
		}
		else{
			WikiContainer section= sections.get(sections.size()-1);
			section.addElement(link);
		}
		
	}

	private WikiLink createLink(Element temp) {

		WikiLink link = new WikiLink();

		String tmp_field = "";
		tmp_field = temp.getAttributeValue("href");
		if (tmp_field != null && !tmp_field.equals("")) {
			link.setHref(tmp_field);
		}

		tmp_field = temp.getAttributeValue("linktype");
		if (tmp_field != null && !tmp_field.equals("")) {
			link.setType(tmp_field);
		}

		tmp_field = temp.getAttributeValue("title");
		if (tmp_field != null && !tmp_field.equals("")) {
			link.setTitle(tmp_field);
		}

		tmp_field = temp.getAttributeValue("pagename");
		if (tmp_field != null && !tmp_field.equals("")) {
			link.setPageName(tmp_field);
		}

		tmp_field = temp.getAttributeValue("page_id");
		if (tmp_field != null && !tmp_field.equals("")) {
			link.setPageId(tmp_field);
		}

		tmp_field = temp.getAttributeValue("fragment");
		if (tmp_field != null && !tmp_field.equals("")) {
			link.setFragment(tmp_field);
		}

		link.setText(temp.getValue());
		// System.out.println("Value added to link:" + temp.getValue());

		return link;

	}

	private void elementListenerPost(Element temp, int depth) {
		// TODO Auto-generated method stub

		String tag = temp.getQualifiedName();

		/**
		 * wx:Section
		 * 
		 * When we finish to parse the tree under some wx:section tag we add it
		 * to the list of elements of the section above it (Recall that sections
		 * might be nested with a maximum recursion of 6 levels)
		 * 
		 * If we finish processing a section of level 1 (first level) we add the
		 * section to the Wikipage object
		 * 
		 */

		if (tag.equals("wx:section")) {

			WikiContainer section = sections.get(sections.size() - 1);

			if (current_container.size() > 0) {
				section.addElement(current_container.pop());

			} else if (current_container.size() > 1) {

				System.out.println("Error cerrando wx:section");
			}

			current_container = new LinkedList<WikiContainer>();
			System.out.println("mirando si me jodi el link:"
					+ section.getElements().size());

		} else if (tag.equals("p") || tag.equals("table")
				|| XOMParserUtils.isHTMLElementTextFormat(tag)) {

			WikiContainer cont = current_container.pop();
			WikiContainer current;

			if (current_container.size() > 0) {

				current = current_container.pop();
				current.addElement(cont);

				current_container.push(current);
			} else {

				WikiContainer section = sections.get(sections.size() - 1);
				section.addElement(cont);
				System.out.println("Strange case in wxsection");

			}

		} else if (tag.equals("th") || tag.equals("td")) {

			// We only get rid of the cell object that is already in the table
			// column
			WikiContainer cont = current_container.pop();

		}
	}

	
	
	
	
	private void updateCurrentSection(WikiContainer element,
			List<WikiContainer> sections) {

		
		WikiContainer tmp = sections.get(sections.size()-1);
		tmp.addElement(element);
		sections.add(tmp);

	}

	private void printSpaces(int n) {

		for (int i = 0; i < n; i++) {
			System.out.print(' ');
		}

	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		WikiParserXOM par = new WikiParserXOM();
		//String path="F:/test/Tiffany__&_Co._10c0.xml";
		par.parseWiki("F:/test/1998_International_Rules_Series_62d1.xml");
		System.out.println("AWUI");
		//par.parseWiki(path);

	}

}
